﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;

namespace Patterns.UnitOfWorkPattern
{
    public class MultiContextUnitOfWork : IMultiContextUnitOfWork
    {
        private readonly IList<IDomainContextUoWAccessor> _Contexts;
        private TransactionScope _TransactionScope;

        public MultiContextUnitOfWork(TransactionScope transactionScope = null)
        {
            _Contexts = new List<IDomainContextUoWAccessor>();
            _TransactionScope = transactionScope ?? new TransactionScope();
        }

        public TransactionScope Transaction
        {
            get
            {
                return _TransactionScope;
            }
        }

        public void SaveChanges()
        {
            foreach (var context in _Contexts)
            {
                context.ApplyChanges();
            }

            if (_TransactionScope != null)
                _TransactionScope.Complete();

            foreach (var context in _Contexts)
            {
                context.Commit();
            }
        }

        public void Rollback()
        {
            foreach (var context in _Contexts)
            {
                context.Rollback();
            }
        }

        public void RegisterContext(IDomainContextUoWAccessor context)
        {
            _Contexts.Add(context);
        }

        public void Dispose()
        {
            foreach (var context in _Contexts)
            {
                context.Dispose();
            }

            _Contexts.Clear();
        }

        public IDomainContext GetContext(Type contextType)
        {
            var context = _Contexts.FirstOrDefault(c => c.GetType() == contextType) as IDomainContext;
            
            if (context != null)
                return context;

            var constructor = contextType.GetConstructor(new[] { typeof(IMultiContextUnitOfWork) });

            if (constructor == null)
                return null;

            context = constructor.Invoke(new object[] { this }) as IDomainContext;

            return context;
        }
    }
}
